package com.segersten.othello.service.rest;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.fail;

import java.io.IOException;
import java.net.URI;
import java.util.List;
import java.util.Random;

import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriBuilder;

import org.testng.annotations.BeforeSuite;
import org.testng.annotations.Test;

import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
import com.google.common.collect.Lists;
import com.segersten.othello.model.Color;
import com.segersten.othello.model.Point;
import com.segersten.othello.service.akka.Auth;
import com.segersten.othello.service.akka.Player;
import com.segersten.othello.service.akka.game.Action;
import com.segersten.othello.service.akka.game.BoardState2;
import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.UniformInterfaceException;
import com.sun.jersey.api.client.WebResource;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;

@Test(groups = { "integration" })
public class OthelloServiceTest2 {
	
	@BeforeSuite
	public void beforeSuite() throws InterruptedException {
		AkkaServerStartupWait.start();
	}

	public void testCreateGame() throws FailingHttpStatusCodeException,
			IOException {
		System.out.println("*********************************");
		System.out.println("testCreateGame");
		System.out.println("*********************************");
		
		WebResource service = getWebResource();
		
		// Create a board
		CreateBoardRequest createBoard = new CreateBoardRequest(new Auth("1", "apa"), getP1(), getP2());
	
		CreateBoardResponse response = service.path("game").path("othello2").path("board").accept(MediaType.APPLICATION_XML).post(CreateBoardResponse.class, createBoard);
		
		BoardState2 boardState = service.path(response.getResource()).accept(MediaType.APPLICATION_XML).get(BoardState2.class);
		assertNotNull(boardState);
	}

	
	public void testPlayGameNotAuthorized() throws FailingHttpStatusCodeException, IOException {

		System.out.println("*********************************");
		System.out.println("testPlayGameNotAuthorized");
		System.out.println("*********************************");
		
		WebResource service = getWebResource();
		
		// Create a board
		CreateBoardRequest createBoard = new CreateBoardRequest(new Auth("1", "apa"), getP1(), getP2());
		CreateBoardResponse response = service.path("game").path("othello2").path("board").accept(MediaType.APPLICATION_XML).post(CreateBoardResponse.class, createBoard);
			
		// Follow resource
		BoardState2 boardState = service.path(response.getResource()).accept(MediaType.APPLICATION_XML).get(BoardState2.class);
		ActionRequest action = new ActionRequest(
				new Auth("node","nope"), new Action(boardState
						.getAvailablePoints().iterator().next(),
						boardState.getNextColor()));
		try {
			service.path(response.getResource()).accept(MediaType.TEXT_PLAIN).post(String.class, action);
			fail("403 expected");
		} catch(UniformInterfaceException e) {
			assertEquals(e.getResponse().getStatus(), 403);
		}
		
		action = new ActionRequest(
				getP1().getAuth(), new Action(boardState
						.getAvailablePoints().iterator().next(),
						boardState.getNextColor().getOpposite()));
		try {
			service.path(response.getResource()).accept(MediaType.TEXT_PLAIN).post(String.class, action);
			fail("409 expected");
		} catch(UniformInterfaceException e) {
			assertEquals(e.getResponse().getStatus(), 409);
		}
		
		action = new ActionRequest(
				getP1().getAuth(), new Action(boardState
						.getAvailablePoints().iterator().next(),
						boardState.getNextColor()));
		service.path(response.getResource()).accept(MediaType.TEXT_PLAIN).post(String.class, action);
	}


	public void testCreateGameNotAuthorized() throws FailingHttpStatusCodeException, IOException {

		WebResource service = getWebResource();

		// Create a board
		CreateBoardRequest createBoard = new CreateBoardRequest(new Auth("nope", "nope"), getP1(), getP2());

		try {
			service.path("game").path("othello2").path("board")
					.accept(MediaType.APPLICATION_XML).post(CreateBoardResponse.class, createBoard);
			fail("403 expected");
		} catch (UniformInterfaceException e) {
			assertEquals(e.getResponse().getStatus(), 403);
		}
	}
	

	public void testPlayGameDeterministicCase()
			throws FailingHttpStatusCodeException, IOException,
			InterruptedException {

		WebResource service = getWebResource();
		
		// Create a board
		CreateBoardRequest createBoard = new CreateBoardRequest(new Auth("1", "apa"), getP1(), getP2());
		CreateBoardResponse response = service.path("game").path("othello2").path("board").accept(MediaType.APPLICATION_XML).post(CreateBoardResponse.class, createBoard);

		// Follow resource
		BoardState2 state = service.path(response.getResource()).accept(MediaType.APPLICATION_XML).get(BoardState2.class);

		int round = 0;
		while (state.getNextColor() != null) {
			round++;
			Player currentPlayer = state.getNextColor().equals(Color.BLACK) ? getP1()
					: getP2();
			ActionRequest action = new ActionRequest(
					currentPlayer.getAuth(), new Action(state
							.getAvailablePoints().iterator().next(),
							state.getNextColor()));
			service.path(response.getResource()).accept(MediaType.TEXT_PLAIN).post(String.class, action);
			state = service.path(response.getResource()).path(Integer.toString(state.getRound())).accept(MediaType.APPLICATION_XML).get(BoardState2.class);
			assertEquals(state.getRound(), round, "" + state);
			assertEquals(state.getTransform().size(), 1, "" + state);
			System.out.println("***********************");
		}
		assertEquals(state.getAvailablePoints().size(), 0);
	}

	public void testPlayGameRandomCase() throws FailingHttpStatusCodeException,
			IOException, InterruptedException {
		System.out.println("************************************************************");
		System.out.println("****************** testPlayGameRandomCase ******************");
		System.out.println("************************************************************");
		
		WebResource service = getWebResource();
		
		// Create a board
		CreateBoardRequest createBoard = new CreateBoardRequest(new Auth("1", "apa"), getP1(), getP2());
		CreateBoardResponse response = service.path("game").path("othello2").path("board").accept(MediaType.APPLICATION_XML).post(CreateBoardResponse.class, createBoard);

		// Follow resource
		BoardState2 state = service.path(response.getResource()).accept(MediaType.APPLICATION_XML).get(BoardState2.class);

		while (state.getNextColor() != null) {
			Action randomAction = getRandomAction(Lists.newArrayList(state.getAvailablePoints()));
			Player currentPlayer = randomAction.getColor().equals(Color.BLACK) ? getP1()
					: getP2();
			ActionRequest action = new ActionRequest( currentPlayer.getAuth(), randomAction);
			try {
				service.path(response.getResource()).accept(MediaType.TEXT_PLAIN).post(String.class, action);
			} catch (UniformInterfaceException e) {
				assertEquals(e.getResponse().getStatus(), 409);
			}
			state = service.path(response.getResource()).path(Integer.toString(state.getRound())).accept(MediaType.APPLICATION_XML).get(BoardState2.class);
			System.out.println("***********************");
		}
		System.out.println("********done***********");
		assertEquals(state.getAvailablePoints().size(), 0,
				"Available points wrong");

		state = service.path(response.getResource()).accept(MediaType.APPLICATION_XML).get(BoardState2.class);
		assertEquals(state.getTransform().size(), state.getRound(),
				"Wierd end state");
	}
	
	////////////////////////////////////////////////////
	///////////////////// PRIVATE //////////////////////
	///////////////////// PRIVATE //////////////////////
	///////////////////// PRIVATE //////////////////////
	////////////////////////////////////////////////////
	
	private WebResource getWebResource() {
		ClientConfig config = new DefaultClientConfig();
		Client client = Client.create(config);
		return client.resource(getBaseURI());
	}

	private URI getBaseURI() {
		return UriBuilder.fromUri(
				"http://localhost:8999").build();
	}

	private Action getRandomAction(List<Point> points) {
		Random random = new Random();
		Color color = random.nextBoolean() ? Color.BLACK : Color.WHITE;
		if (random.nextInt(100) < 1) {
			return new Action(null, color);
		}
		Point point = points.isEmpty() ? null : points.get(random
				.nextInt(points.size()));
		return new Action(point, color);
	}

	private Player getP2() {
		return new Player("p2", new Auth("p2", "123"));
	}

	private Player getP1() {
		return new Player("p1", new Auth("p1", "123"));
	}

}
